在現代軟體開發和部署的複雜生態中,服務的準備和初始化往往需要一系列精心編排的步驟。從環境配置到依賴服務的啟動,每一個階段都是確保應用順利運行的關鍵。Kubernetes深諳這一挑戰,並提供了一個優雅的解決方案:Init Container。
Init Container
(Init 容器) 是 Kubernetes 中的一種特別設計,它的任務是在應用容器啟動之前完成初始化工作。這些 Init Container 可以負責一系列準備工作,例如配置環境變數、檢查依賴服務是否已啟動等。每個 Init Container 都必須成功完成後,Kubernetes 才會啟動下一個 Init Container 或最終的應用容器。這種機制確保了應用可以在一個準備妥當的環境中穩定運行。
Init Container 雖然與普通容器非常相似,但有以下兩個重要的區別:
即使 Init 容器會不斷重試,但如果 Pod 的 restartPolicy
被設置為 "Never",當 Init 容器失敗時,Pod 狀態會被標記為失敗。
此外,Init 容器支援應用容器的所有功能,包括資源限制、資料卷掛載和安全配置。不過,Init 容器對資源請求和限制的處理方式略有不同,詳細說明請參見 Init 容器與 Pod 資源請求和限制的處理。
Init 容器的設計目的是快速完成初始化任務並退出,而應用容器負責持續運行應用程式,因此 Probe
和 lifecycle
配置不適用於 Init 容器。
如果一個 Pod 定義了多個 Init 容器,這些容器會按順序執行,且只有當前的 Init 容器成功運行後,下一個 Init 容器才會啟動。所有的 Init 容器都運行完成後,Kubernetes 才會初始化 Pod 並啟動應用容器。
Init 容器有獨立的映像檔,並能使用與應用容器分離的啟動程式碼,這帶來了許多優勢:
sed
、awk
等),這避免了為應用容器增加不必要的工具或擴大映像檔大小。簡單來說,Init 容器讓應用和初始化邏輯分離,應用容器可以專注於最核心的應用邏輯,並以最少的權限運行。
Init 容器也可以定義資源請求和限制,不過它的處理方式與應用容器有所不同。以下是一個示例:
init1
請求 100m CPU,限制 200m CPU。init2
請求 150m CPU,限制 250m CPU。app1
請求 300m CPU,限制 500m CPU。app2
請求 200m CPU,限制 300m CPU。資源計算的邏輯如下:
init2
的請求)。init2
的限制)。app1
和 app2
的請求總和)。app1
和 app2
的限制總和)。因此,Pod 的有效資源計算如下:
這種資源計算方式確保了 Init 容器能夠獲取足夠的資源來執行初始化任務,而應用容器則能在資源充足的情況下穩定運行。
組態檔案: pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
上述的組態檔案定義了一個具有 2 個 Init 容器的簡單 Pod。 第一個等待 myservice
啟動, 第二個等待 mydb
啟動。 一旦這兩個 Init 容器都啟動完成,Pod 將啟動應用容器。
kubectl apply -f pod.yaml
kubectl get pod myapp-pod
---
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 55s
kubectl describe pod myapp-pod
結果如下
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m19s default-scheduler Successfully assigned default/myapp-pod to wslkind-worker2
Normal Pulled 3m19s kubelet Container image "busybox:1.28" already present on machine
Normal Created 3m19s kubelet Created container init-myservice
Normal Started 3m19s kubelet Started container init-myservice
可以看到,目前的 Pod 的應用容器狀態都是 Waiting
,Init 容器將會等到發現名稱為 mydb
和 myservice
的服務後,才會完成並關閉容器。
現在我們來建立 mydb
和 myservice
服務。
組態檔案: service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
kubectl apply -f service.yaml
kubectl get pods myapp-pod
---
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m23s
kubectl describe pod myapp-pod
結果如下
[...]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 9m44s default-scheduler Successfully assigned default/myapp-pod to wslkind-worker2
Normal Pulled 9m44s kubelet Container image "busybox:1.28" already present on machine
Normal Created 9m44s kubelet Created container init-myservice
Normal Started 9m44s kubelet Started container init-myservice
Normal Pulled 27s kubelet Container image "busybox:1.28" already present on machine
Normal Created 27s kubelet Created container init-mydb
Normal Started 27s kubelet Started container init-mydb
Normal Pulled 26s kubelet Container image "busybox:1.28" already present on machine
Normal Created 26s kubelet Created container myapp-container
Normal Started 26s kubelet Started container myapp-container
可以看到,建立 mydb
和 myservice
服務後,init-myservice
容器便完成,緊接著啟動並執行 init-mydb
容器,等待它也完成後,接著應用容器 myapp-container
便開始運作。
Init Container 透過多容器 Pod 的特性,不僅解決了傳統容器化應用中的初始化難題,還為開發者提供了更大的靈活性和控制力。通過將初始化邏輯與主應用邏輯分離,Init Container 促進了更清晰的責任劃分和更高的系統可靠性。
在實際部署中,Init Container 的應用範圍廣泛,從簡單的環境檢查到複雜的數據預處理都可以輕鬆實現。它的存在使得容器化應用的生命週期管理更加精細和可控,為構建健壯、可擴展的雲原生應用奠定了堅實基礎。